package fmj.lib

import fmj.characters.Monster
import fmj.characters.NPC
import fmj.characters.Player
import fmj.characters.ResLevelupChain
import fmj.characters.SceneObj
import fmj.goods.BaseGoods
import fmj.goods.GoodsDecorations
import fmj.goods.GoodsDrama
import fmj.goods.GoodsEquipment
import fmj.goods.GoodsHiddenWeapon
import fmj.goods.GoodsMedicine
import fmj.goods.GoodsMedicineChg4Ever
import fmj.goods.GoodsMedicineLife
import fmj.goods.GoodsStimulant
import fmj.goods.GoodsTudun
import fmj.goods.GoodsWeapon
import fmj.magic.*
import fmj.script.ScriptVM
import java.File


class DatLib(buffer: ByteArray) {

    /**
     * DAT.LIB文件的所有内容
     */
    private var mBuffer = buffer

    /**
     * 保存资源数据相对文件首字节的偏移量
     */
    private val mDataOffset = HashMap<Int, Int>(2048)

    data class Res(val type: Int, val index: Int)
    private val guts = arrayListOf<Res>()

    init {
        getAllResOffset()
    }

    fun tryCompileScripts(vm: ScriptVM) {
        println("Trying to compile all guts")
        guts.forEach {
            vm.loadScript(it.type, it.index)
        }
        println("All guts compile OK")
    }

    private fun getAllResOffset() {
        var i = 0x10
        var j = 0x2000

        while (i < mBuffer.size && mBuffer[i].toInt() != -1) {
            val resType = mBuffer[i++].toInt()
            val type = mBuffer[i++].toInt()
            val index = mBuffer[i++].toInt() and 0xFF
            val key = getKey(resType, type, index)
            val block = mBuffer[j++].toInt() and 0xFF
            val low = mBuffer[j++].toInt() and 0xFF
            val high = mBuffer[j++].toInt() and 0xFF
            val value = block * 0x4000 or (high shl 8 or low)
            mDataOffset.put(key, value)
            if (resType == 1) {
                guts.add(Res(type, index))
            }
        }
    }

    /**
     *
     * @param resType
     * 资源文件类型号1-12
     * @param type
     * 资源类型
     * @param index
     * 资源索引号
     * @return 资源对象，不存在则返回`null`
     */
    fun getRes(resType: ResType, type: Int, index: Int, allowNull: Boolean = false): ResBase? {
        val offset = getDataOffset(resType, type, index)
        val res = (if (offset != -1) {
            val res: ResBase? =
                    when (resType) {
                        ResType.GUT -> ResGut()

                        ResType.MAP -> ResMap()

                        ResType.ARS -> when (type) {
                            1 // 玩家角色
                            -> Player()

                            2 // NPC角色
                            -> NPC()

                            3 // 敌人角色
                            -> Monster()

                            4 // 场景对象
                            -> SceneObj()

                            else -> null
                        }

                        ResType.MRS -> getMagic(type)

                        ResType.SRS -> ResSrs()

                        ResType.GRS -> getGoods(type)

                        ResType.TIL, ResType.ACP, ResType.GDP, ResType.GGJ, ResType.PIC -> ResImage()

                        ResType.MLR -> when(type) {
                            1 -> ResMagicChain()
                            2 -> ResLevelupChain()
                            else -> null
                        }
                    }
            res?.setData(mBuffer, offset)
            res
        } else {
            // 资源不存在
            null
        })
        if (allowNull) return res
        return res ?: throw Error("res not found:resType=$resType,type=$type,index=$index")
    }

    private fun getGoods(type: Int): BaseGoods? {
        if (type in 1..5) {
            return GoodsEquipment()
        }
        var rtn: BaseGoods? = null
        when (type) {
            6 -> rtn = GoodsDecorations()

            7 -> rtn = GoodsWeapon()

            8 -> rtn = GoodsHiddenWeapon()

            9 -> rtn = GoodsMedicine()

            10 -> rtn = GoodsMedicineLife()

            11 -> rtn = GoodsMedicineChg4Ever()

            12 -> rtn = GoodsStimulant()

            13 -> rtn = GoodsTudun()

            14 -> rtn = GoodsDrama()
        }
        return rtn
    }

    private fun getMagic(type: Int): ResBase? {
        when (type) {
            1 -> return MagicAttack()
            2 -> return MagicEnhance()
            3 -> return MagicRestore()
            4 -> return MagicAuxiliary()
            5 -> return MagicSpecial()
        }
        return null
    }

    /**
     *
     * @param resType
     * 资源文件类型号1-12
     * @param type
     * 资源类型
     * @param index
     * 资源索引号
     * @return 资源所在位置, 返回-1表示不存在
     */
    private fun getDataOffset(resType: ResType, type: Int, index: Int): Int {
        return mDataOffset[getKey(resType.v, type, index)] ?: -1
    }

    /**
     *
     * @param resType
     * 资源文件类型号1-12
     * @param type
     * 资源类型
     * @param index
     * 资源索引号
     * @return 每个资源唯一的编号，用于哈希表键
     */
    private fun getKey(resType: Int, type: Int, index: Int): Int {
        return resType shl 16 or (type shl 8) or index
    }

    enum class ResType(val v: Int) {
        GUT(1), // 剧情脚本
        MAP(2), // 地图资源
        ARS(3), // 角色资源
        MRS(4), // 魔法资源
        SRS(5), // 特效资源
        GRS(6), // 道具资源
        TIL(7), // tile资源
        ACP(8), // 角色图片
        GDP(9), // 道具图片
        GGJ(10), // 特效图片
        PIC(11), // 杂类图片
        MLR(12), // 链资源
    }

    companion object {
        val instance: DatLib by lazy {
            DatLib(File.contentsOf("DAT.LIB"))
        }

        fun getRes(resType: ResType, type: Int, index: Int, allowNull: Boolean = false): ResBase? {
            return instance.getRes(resType, type, index, allowNull)
        }

        fun getPic(type: Int, index: Int, allowNull: Boolean = false): ResImage? {
            return getRes(ResType.PIC, type, index, allowNull) as ResImage?
        }

        fun getMlr(type: Int, index: Int, allowNull: Boolean = false): ResMagicChain? {
            return getRes(ResType.MLR, type, index, allowNull) as ResMagicChain?
        }

        fun getACP(type: Int, index: Int, allowNull: Boolean = false): ResImage? {
            return getRes(ResType.ACP, type, index, allowNull) as ResImage?
        }

        fun getMrsOrNull(type: Int, index: Int): BaseMagic? {
            return getRes(ResType.MRS, type, index, true) as BaseMagic?
        }

        fun getMrs(type: Int, index: Int): BaseMagic {
            return getRes(ResType.MRS, type, index, false) as BaseMagic
        }
    }
}
